#include "net/ip_addr.hh"
#include <system_error>

namespace wb::net
{
static char const domain_ipv4[5] = "ipv4";
static char const domain_ipv6[5] = "ipv6";

std::string_view to_string(domain_t d)
{
    switch (d)
    {
        case domain_t::IPv4:
            return domain_ipv4;
        case domain_t::IPv6:
            return domain_ipv6;
    }
    throw std::runtime_error("wb::net::to_string - Unknown domain");
}

ip_address::ip_address(std::span<const uint8_t> bin_addr, domain_t d)
    : domain_{d}
{
    if (domain_ == domain_t::IPv4 && bin_addr.size() > ipv4_len)
    {
        throw std::runtime_error(
            "wb::net::ip_address() binary ipaddr too long for ipv4");
    }
    else if (domain_ == domain_t::IPv6 && bin_addr.size() > ipv6_len)
    {
        throw std::runtime_error(
            "wb::net::ip_address() binary ipaddr too long for ipv6");
    }
    std::copy(bin_addr.begin(), bin_addr.end(), data_.begin());
}

ip_address ip_address::from_string(const std::string& address, domain_t d)
{
    ip_address addr{};
    addr.domain_ = d;
    auto ret =
        ::inet_pton(static_cast<int>(d), address.c_str(), addr.data_.data());
    if (ret != 1)
    {
        throw std::runtime_error("wb::net::from_string convert failed");
    }
    return addr;
}

std::string ip_address::to_string() const
{
    socklen_t len = domain_ == domain_t::IPv4 ? ipv4_strlen : ipv6_strlen;
    std::string buffer;
    buffer.resize(ipv6_strlen);
    // since C++17, std::string::data() returns char* instead of const char*
    auto ret = ::inet_ntop(static_cast<int>(domain_),
                           data_.data(),
                           buffer.data(),
                           len);
    if (ret == nullptr)
    {
        throw std::system_error(errno, std::system_category(), "inet_ntop()");
    }

    return buffer;
}

}  // namespace wb::net